package com.polaris.live.im.utils

import com.polaris.live.common.util.ReflexUtils
import com.smartfoxserver.v2.entities.data.*
import com.smartfoxserver.v2.protocol.serialization.DefaultSFSDataSerializer
import org.apache.commons.lang.SerializationException
import sfs2x.client.entities.Room
import sfs2x.client.entities.User
import sfs2x.client.entities.variables.RoomVariable
import java.util.Collections
import kotlin.reflect.KClass

/**
 * SmartFoxUtils
 *
 * @author Created by lucas on 2023/10/19 11:15
 */
object SmartFoxUtils {

    /**
     * 解析user的variables
     */
    inline fun <reified T : Any> User.parseVariables(): T? {
        val map = buildMap {
            variables.forEach {
                put(it.name, it.value)
            }
        }

        return ReflexUtils.fromMap(map)
    }

    /**
     * 解析room的variables
     */
    inline fun <reified T : Any> Room.parseVariables(): T? {
        val map = buildMap {
            variables.forEach {
                put(it.name, it.value)
            }
        }

        return ReflexUtils.fromMap(map)
    }

    /**
     * 将SFSObject数据转换为自定义的数据
     */
    inline fun <reified T : Any> fromSFSObject(sfsObject: SFSObject): T? {
        return fromSFSObject(sfsObject, T::class)
    }

    /**
     * 将SFSObject数据转换为自定义的数据
     */
    fun <T : Any> fromSFSObject(sfsObject: SFSObject, kClass: KClass<T>): T? {
        return flatten(sfsObject).let {
            ReflexUtils.fromMap(it, kClass)
        }
    }

    /**
     * 将自定义的数据转换为SFSObject
     */
    fun <T : Any> toSFSObject(obj: T): SFSObject {
        val map = ReflexUtils.toMap(obj)

        return SFSObject().apply {
            map.forEach { (key, value) ->
                put(key, encodeSfsObject(value))
            }
        }
    }

    /**
     * sfsObject转换为map，递归寻找
     *
     * @param sfsObject 对象
     */
    fun flatten(sfsObject: SFSObject): Map<String, Any> {
        return HashMap<String, Any>().apply {
            DefaultSFSDataSerializer.getInstance().flattenObject(this, sfsObject)
        }
    }

    /**
     * sfsArray转换为list，递归寻找
     *
     * @param sfsArray 对象
     */
    fun flatten(sfsArray: SFSArray): List<Any> {
        return ArrayList<Any>().apply {
            DefaultSFSDataSerializer.getInstance().flattenArray(this, sfsArray)
        }
    }

    /**
     * 编码jsonObject类型
     */
    private fun encodeSfsObject(obj: Any): SFSDataWrapper {
        val clazz = obj.javaClass
        val wrapper = if (!clazz.isPrimitive) {
            when {
                java.lang.CharSequence::class.java.isAssignableFrom(clazz) -> {
                    SFSDataWrapper(SFSDataType.UTF_STRING, obj)
                }
                clazz == java.lang.Byte::class.java -> {
                    SFSDataWrapper(SFSDataType.BYTE, obj)
                }
                clazz == java.lang.Short::class.java -> {
                    SFSDataWrapper(SFSDataType.SHORT, obj)
                }
                clazz == java.lang.Integer::class.java -> {
                    SFSDataWrapper(SFSDataType.INT, obj)
                }
                clazz == java.lang.Long::class.java -> {
                    SFSDataWrapper(SFSDataType.LONG, obj)
                }
                clazz == java.lang.Float::class.java -> {
                    SFSDataWrapper(SFSDataType.FLOAT, obj)
                }
                clazz == java.lang.Double::class.java -> {
                    SFSDataWrapper(SFSDataType.DOUBLE, obj)
                }
                clazz == java.lang.Boolean::class.java -> {
                    SFSDataWrapper(SFSDataType.BOOL, obj)
                }
                else -> formatArrayType(obj, clazz)
            }
        } else {
            when (clazz) {
                java.lang.Byte.TYPE -> {
                    SFSDataWrapper(SFSDataType.BYTE, obj)
                }
                java.lang.Short.TYPE -> {
                    SFSDataWrapper(SFSDataType.SHORT, obj)
                }
                Integer.TYPE -> {
                    SFSDataWrapper(SFSDataType.INT, obj)
                }
                java.lang.Long.TYPE -> {
                    SFSDataWrapper(SFSDataType.LONG, obj)
                }
                java.lang.Float.TYPE -> {
                    SFSDataWrapper(SFSDataType.FLOAT, obj)
                }
                java.lang.Double.TYPE -> {
                    SFSDataWrapper(SFSDataType.DOUBLE, obj)
                }
                java.lang.Boolean.TYPE -> {
                    SFSDataWrapper(SFSDataType.BOOL, obj)
                }
                else -> null
            }
        }

        return wrapper ?: throw SerializationException("Class<${clazz.simpleName}> type is not supported.")
    }

    /**
     * 获取smartFox数组类型
     *
     * ps：byte会被转换为基本类型
     */
    @Suppress("UNCHECKED_CAST")
    private fun formatArrayType(obj: Any, clazz: Class<*>): SFSDataWrapper? {
        return if (Collection::class.java.isAssignableFrom(clazz)) {
            toSfsArray(obj as Collection<*>)
        } else if (clazz == Array<Boolean>::class.java) {
            val src = obj as Array<Boolean>
            val dest = ArrayList<Boolean>(src.size)
            Collections.addAll(dest, *src)
            SFSDataWrapper(SFSDataType.BOOL_ARRAY, dest)
        } else if (clazz.isAssignableFrom(BooleanArray::class.java)) {
            val src = obj as BooleanArray
            val dest: MutableList<Boolean> = ArrayList(src.size)
            for (s in src) {
                dest.add(s)
            }
            SFSDataWrapper(SFSDataType.BOOL_ARRAY, dest)
        } else if (clazz == ByteArray::class.java) {
            SFSDataWrapper(SFSDataType.BYTE_ARRAY, obj)
        } else if (clazz.isAssignableFrom(Array<Byte>::class.java)) {
            toSfsArray(obj as Array<Byte>)
        } else if (clazz == Array<Short>::class.java) {
            val src = obj as Array<Short>
            val dest = ArrayList<Short>(src.size)
            Collections.addAll(dest, *src)
            return SFSDataWrapper(SFSDataType.SHORT_ARRAY, dest)
        } else if (clazz.isAssignableFrom(ShortArray::class.java)) {
            val src = obj as ShortArray
            val dest: MutableList<Short> = ArrayList(src.size)
            for (s in src) {
                dest.add(s)
            }
            return SFSDataWrapper(SFSDataType.SHORT_ARRAY, dest)
        } else if (clazz == Array<Int>::class.java) {
            val src = obj as Array<Int>
            val dest = ArrayList<Int>(src.size)
            Collections.addAll(dest, *src)
            return SFSDataWrapper(SFSDataType.INT_ARRAY, dest)
        } else if (clazz.isAssignableFrom(IntArray::class.java)) {
            val src = obj as IntArray
            val list: MutableList<Int> = ArrayList(src.size)
            for (s in src) {
                list.add(s)
            }
            return SFSDataWrapper(SFSDataType.INT_ARRAY, list)
        } else if (clazz == Array<Long>::class.java) {
            val src = obj as Array<Long>
            val dest = ArrayList<Long>(src.size)
            Collections.addAll(dest, *src)
            return SFSDataWrapper(SFSDataType.LONG_ARRAY, dest)
        } else if (clazz.isAssignableFrom(LongArray::class.java)) {
            val src = obj as LongArray
            val list: MutableList<Long> = ArrayList(src.size)
            for (s in src) {
                list.add(s)
            }
            return SFSDataWrapper(SFSDataType.LONG_ARRAY, list)
        } else if (clazz == Array<Float>::class.java) {
            val src = obj as Array<Float>
            val dest = ArrayList<Float>(src.size)
            Collections.addAll(dest, *src)
            return SFSDataWrapper(SFSDataType.FLOAT_ARRAY, dest)
        } else if (clazz.isAssignableFrom(FloatArray::class.java)) {
            val src = obj as FloatArray
            val list: MutableList<Float> = ArrayList(src.size)
            for (s in src) {
                list.add(s)
            }
            return SFSDataWrapper(SFSDataType.FLOAT_ARRAY, list)
        } else if (clazz == Array<Double>::class.java) {
            val src = obj as Array<Double>
            val dest = ArrayList<Double>(src.size)
            Collections.addAll(dest, *src)
            return SFSDataWrapper(SFSDataType.DOUBLE_ARRAY, dest)
        } else if (clazz.isAssignableFrom(DoubleArray::class.java)) {
            val src = obj as DoubleArray
            val list = ArrayList<Double>(src.size)
            for (s in src) {
                list.add(s)
            }
            return SFSDataWrapper(SFSDataType.DOUBLE_ARRAY, list)
        } else if (clazz == Array<String>::class.java) {
            val src = obj as Array<String>
            val dest = ArrayList<String>(src.size)
            Collections.addAll(dest, *src)
            return SFSDataWrapper(SFSDataType.UTF_STRING_ARRAY, dest)
        } else {
            return null
        }
    }

    private fun toSfsArray(src: Array<*>): SFSDataWrapper {
        val array = SFSArray()
        for (s in src) {
            s?.apply {
                formatType(s)?.also { array.add(it) }
            }
        }
        return SFSDataWrapper(SFSDataType.SFS_ARRAY, array)
    }

    private fun toSfsArray(src: Collection<*>): SFSDataWrapper {
        val array = SFSArray()
        for (s in src) {
            s?.apply {
                formatType(s)?.also { array.add(it) }
            }
        }
        return SFSDataWrapper(SFSDataType.SFS_ARRAY, array)
    }

    /**
     * 获取smartFox类型
     *
     * @param `object` 值
     */
    private fun formatType(obj: Any): SFSDataWrapper? {
        val clazz: Class<*> = obj.javaClass
        val type = convertType(clazz)
            ?: return null

        return SFSDataWrapper(type, obj)
    }

    /**
     * 获取smartFox类型
     *
     * @param clazz 类型
     */
    private fun convertType(clazz: Class<*>): SFSDataType? {
        return when (clazz) {
            java.lang.Boolean::class.java -> SFSDataType.BOOL
            java.lang.Byte::class.java -> SFSDataType.BYTE
            java.lang.Short::class.java -> SFSDataType.SHORT
            java.lang.Integer::class.java -> SFSDataType.INT
            java.lang.Long::class.java -> SFSDataType.LONG
            java.lang.Float::class.java -> SFSDataType.FLOAT
            java.lang.Double::class.java -> SFSDataType.DOUBLE
            java.lang.String::class.java -> SFSDataType.UTF_STRING
            ISFSObject::class.java -> SFSDataType.SFS_OBJECT
            ISFSArray::class.java -> SFSDataType.SFS_ARRAY
            else -> return null
        }
    }

    fun <T> parseSfsObject(wrapper: SFSDataWrapper?, clazz: Class<T>): T? {
        if (wrapper == null) {
            return null
        }
        return if (parseSfsObjectType(wrapper.typeId) != clazz) {
            null
        } else {
            wrapper.getObject() as T
        }
    }

    private fun parseSfsObjectType(type: SFSDataType?): Class<*>? {
        return when (type) {
            SFSDataType.BOOL -> java.lang.Boolean::class.java
            SFSDataType.BYTE -> java.lang.Byte::class.java
            SFSDataType.SHORT -> java.lang.Short::class.java
            SFSDataType.INT -> java.lang.Integer::class.java
            SFSDataType.LONG -> java.lang.Long::class.java
            SFSDataType.FLOAT -> java.lang.Float::class.java
            SFSDataType.DOUBLE -> java.lang.Double::class.java
            SFSDataType.UTF_STRING -> java.lang.String::class.java
            SFSDataType.SFS_ARRAY -> java.util.ArrayList::class.java
            SFSDataType.SFS_OBJECT -> SFSObject::class.java
            else -> null
        }
    }
}

/**
 * 转换为boolean
 */
fun RoomVariable.toBoolean(): Boolean {
    return toBooleanOrNull() ?: false
}

/**
 * 转换为boolean
 */
fun RoomVariable.toBooleanOrNull(): Boolean? {
    return value as? Boolean
}

/**
 * 转换为int
 */
fun RoomVariable.toInt(): Int {
    return toIntOrNull() ?: 0
}

/**
 * 转换为int
 */
fun RoomVariable.toIntOrNull(): Int? {
    return (value as? Number)?.toInt()
}

/**
 * 转换为long
 */
fun RoomVariable.toLong(): Long {
    return toLongOrNull() ?: 0L
}

/**
 * 转换为long
 */
fun RoomVariable.toLongOrNull(): Long? {
    return (value as? Number)?.toLong()
}

/**
 * 转换为string
 */
fun RoomVariable.toString(): String {
    return toStringOrNull() ?: ""
}

/**
 * 转换为string
 */
fun RoomVariable.toStringOrNull(): String? {
    return value as? String
}